﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Projektarbete
{
    static class PerlinNoise
    {
        public static float[,] GenerateWhiteNoise(int width, int height)
        {
            Random random = new Random();

            float[,] noise = new float[width, height];

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    noise[x, y] = (float)random.NextDouble() % 1;
                }
            }

            return noise;
        }

        public static float[,] GenerateSmoothNoise(float[,] baseNoise, int octave)
        {
            int width = baseNoise.GetLength(0);
            int height = baseNoise.GetLength(1);

            float[,] smoothNoise = new float[width, height];

            int samplePeriod = 1 << octave;
            float sampleFrequency = 1.0f / samplePeriod;

            for (int x = 0; x < width; x++)
            {
                //TODO
                int sampleX0 = (x / samplePeriod) * samplePeriod;
                int sampleX1 = (sampleX0 + samplePeriod) % width;
                float horizontalBlend = (x - sampleX0) * sampleFrequency;

                for (int y = 0; y < height; y++)
                {
                    int sampleY0 = (y / samplePeriod) * samplePeriod;
                    int sampleY1 = (sampleY0 + samplePeriod) % height;
                    float verticalBlend = (y - sampleY0) * sampleFrequency;

                    float top = Interpolate(baseNoise[sampleX0, sampleY0], baseNoise[sampleX1, sampleY0], horizontalBlend);

                    float bottom = Interpolate(baseNoise[sampleX0, sampleY1], baseNoise[sampleX1, sampleY1], horizontalBlend);

                    smoothNoise[x, y] = Interpolate(top, bottom, verticalBlend);
                }
            }

            return smoothNoise;
        }

        public static float Interpolate(float x0, float x1, float alpha)
        {
            return x0 * (1 - alpha) + alpha * x1;
        }

        public static float[,] GeneratePerlinNoise(float[,] baseNoise, int octaveCount)
        {
            int width = baseNoise.GetLength(0);
            int height = baseNoise.GetLength(1);

            float[][,] smoothNoise = new float[octaveCount][,];

            float persistance = 0.5f;

            for (int x = 0; x < octaveCount; x++)
            {
                smoothNoise[x] = GenerateSmoothNoise(baseNoise, x);
            }

            float[,] perlinNoise = new float[width, height];

            float amplitude = 1.0f;
            float totalAmplitude = 0.0f;

            for (int octave = octaveCount - 1; octave >= 0; octave--)
            {
                amplitude *= persistance;
                totalAmplitude += amplitude;

                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        perlinNoise[x, y] += smoothNoise[octave][x, y] * amplitude;
                    }
                }
            }

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    perlinNoise[x, y] /= totalAmplitude;
                }
            }

            return perlinNoise;
        }

        public static Color GetColor(Color gradientStart, Color gradientEnd, float t)
        {
            float u = 1 - t;

            Color color = new Color((int)(gradientStart.R * u + gradientEnd.R * t),
                                    (int)(gradientStart.G * u + gradientEnd.G * t),
                                    (int)(gradientStart.B * u + gradientEnd.B * t), 255);

            return color;
        }

        public static Color[,] MapGradient(Color gradientStart, Color gradientEnd, float[,] perlinNoise)
        {
            int width = perlinNoise.GetLength(0);
            int height = perlinNoise.GetLength(1);

            Color[,] image = new Color[width, height];

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    image[x, y] = GetColor(gradientStart, gradientEnd, perlinNoise[x, y]);
                }
            }

            return image;
        }
    }
}
